package com.golf.server.engine.phys;

import net.phys2d.math.ROVector2f;
import net.phys2d.math.Vector2f;
import net.phys2d.raw.Body;
import net.phys2d.raw.BodyList;
import net.phys2d.raw.CollisionEvent;
import net.phys2d.raw.CollisionListener;
import net.phys2d.raw.World;
import net.phys2d.raw.shapes.Circle;
import net.phys2d.raw.strategies.QuadSpaceStrategy;

import com.golf.core.course.Course;
import com.golf.core.course.bodies.Ball;
import com.golf.core.course.bodies.CourseBodies;
import com.golf.core.course.bodies.CourseBody;
import com.golf.core.course.bodies.MovableCourseBody;
import com.golf.core.course.bodies.Position;
import com.golf.core.course.bodies.Vector;


public class Engine implements CollisionListener
{

    private final Course course;
    
    private boolean isFinished;
    
    
    private World world;
    
    
    
    public Engine(Course course)
    {
        this.course = course;
        this.isFinished = true;
    }

    
    public void reset()
    {
        isFinished = false;
        
        //w = new World(new Vector2f(0,0), 100);
        world = new World(new Vector2f(0.0f, 0.0f), 10, new QuadSpaceStrategy(20,5));
        world.clear();
        world.setGravity(0,0);        
        world.addListener(this);
        world.enableRestingBodyDetection(0.01f, 0.000001f, 100f);

        System.out.println("Resetting world - adding body of count - " + course.getBodies().getBodies().size());
        
        final CourseBodies bodies = course.getBodies();
        for (CourseBody body : bodies.getBodies())
        {
            System.out.println("Adding body to engine of type: " + body.getClass().getName());
        	Body b = body.asPhysicsBody();
        	if (b != null)
        	{	
        	    bodies.updateNamedBodyWithId(body, b.getID()); // NB: bad API refactor this !! the course bodies should already know the id.
        		world.add(b);
        		
        	}
        }
    }
    


    private Body creaetBall(String name, Position position, float size, float weight, float restitution, float friction)
    {
		Body body = new Body(name, new Circle(size), weight);
        body.setPosition(position.getX(), position.getY());
        body.setRestitution(restitution);
        body.setFriction(friction);
        body.setRotDamping(0.2f);
        body.setCanRest(true);

        return body;
    }


	public void step()
    {
       
        world.step();
        BodyList bl = world.getBodies();
        CourseBodies bodies = course.getBodies();

        System.out.println("in setp - engine body list size - " + bl.size());
        boolean allstopped = true;
        for (int p = 0 ; p < bl.size() ; p++)
        {
            Body b = bl.get(p);
            if (!b.isResting())
            {
                allstopped = false;
            }
            else
            {
//                System.out.println(b.getID() + " is stopped");
            }
            
            ROVector2f v = b.getVelocity();
            System.out.println("speed: " + v.getX() + ", " + v.getY());
            
            // update postition
            if (b.getShape() instanceof Circle)
            {
    //            CourseBody cb = (CourseBody)bodies.getBodyById(b.getID());
               // FIXME: the above is borken !! 
                
            	/** replace me with above */
            	final String ballId = String.valueOf(b.getID());
            	
            	CourseBody cb = (CourseBody)bodies.getBodyByName(ballId);
                if (cb == null)
                {
                    cb = new Ball(ballId, new Position(b.getPosition().getX(),b.getPosition().getY()) );
                    bodies.add(cb);
                }
            	/** replace me */

                if (cb instanceof Ball)
                {
                    MovableCourseBody mcb = (MovableCourseBody)cb;
                    mcb.setPosition(b.getPosition().getX(), b.getPosition().getY());
                }    
            }
        }

//        if (allstopped == true)
//        {
//            isFinished = true;
//        }

      isFinished = false;
        
        
    }
    
	/**
	 * {@inheritDoc}
	 */
    public void collisionOccured(CollisionEvent evt)
    {
        // Do nothing currently.
        // In future, if collision is ball + oneof(hole, water, etc.) then do special action.
    }

    public boolean isFinished()
    {
        return isFinished;
    }


	private void hit(Ball ball, Vector aimVector) 
	{
	    // Translate the aim vector into a "force" to apply to the ball.
	    float top = 0;
	    float right = 0;
	    
        top = -50 * (float)(aimVector.getSpeed() * Math.sin(aimVector.getAngle()));
        right = 50 * (float)(aimVector.getSpeed() * Math.cos(aimVector.getAngle()));
	    
	    
        float size = 10f;
        float weight = 2.0f;
        float restitution = 1.0f;
        float friction = 10.0f;

        Body b = creaetBall("weight4", ball.getPosition(), size, weight, restitution, friction);
        b.addForce(new Vector2f(top, right));
        
        world.add(b);
		
	}


    public void playShot(Ball ball, Vector shot)
    {
        System.out.println("Playing shot with ball -> " + ball.name());
        hit(ball, shot);
        
        for (int i = 0 ; i < 5000 ; i++)
        {            
            this.step();
            // NB: the above will be replaced with a "check for updates from server"
            // wait type action
            
            // Message Types will maybe be:
                // Object moved (name,+x,+y)
                // Object stoped moving (name,+x, +y)
        }
        

    }
        

    
}